package com.shuai.gateway.filter;


import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import com.shuai.common.constant.AuthenticationConstant;
import com.shuai.common.constant.ErrorCode;
import com.shuai.common.constant.UserConstant;
import com.shuai.common.exception.BusinessException;
import com.shuai.common.utils.MyJWTUtil;
import com.shuai.gateway.config.AuthProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.time.LocalDateTime;
import java.util.Date;

@Component
@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Autowired
    private AuthProperties authProperties;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        // 记录请求日志
        this.requestLog(request);
        if (this.exclude(request.getPath().toString())) {
            return chain.filter(exchange);
        }
        // 根据请求做鉴权
        this.doAuthentication(request);
        return chain.filter(exchange);
    }

    private boolean exclude(String path) {
        return authProperties.getExcludeUri().contains(path);
    }


//    /**
//     * 得到下游接口响应后的后续操作
//     */
//    private Mono<Void> handleResponse(ServerWebExchange exchange, GatewayFilterChain chain) {
//
//        try {
//            StopWatch stopWatch = new StopWatch();
//            stopWatch.start();
//            ServerHttpResponse originalResponse = exchange.getResponse();       // 得到转发接口的响应
//            DataBufferFactory bufferFactory = originalResponse.bufferFactory(); // 拿到响应体的缓冲区工厂
//            HttpStatus statusCode = originalResponse.getStatusCode();
//            if (statusCode == HttpStatus.OK) {
//                ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
//                    @Override
//                    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
//                        log.info("body instanceof Flux: {}", (body instanceof Flux));
//                        if (body instanceof Flux) {
//                            Flux<? extends DataBuffer> fluxBody = Flux.from(body);
//                            //
//                            return super.writeWith(fluxBody.map(dataBuffer -> {
//                                // 统计接口响应时间
//                                stopWatch.stop();
//                                byte[] content = new byte[dataBuffer.readableByteCount()];
//                                dataBuffer.read(content);
//                                DataBufferUtils.release(dataBuffer);//释放掉内存
//                                // 记录响应日志
//                                responseLog(exchange.getRequest().getId(), content, originalResponse.getStatusCode());
//                                return bufferFactory.wrap(content);
//                            }));
//                        } else {
//                            log.error("<--- {} 响应code异常", getStatusCode());
//                        }
//                        return super.writeWith(body);
//                    }
//                };
//                return chain.filter(exchange.mutate().response(decoratedResponse).build());
//            }
//            return chain.filter(exchange);//降级处理返回数据
//        } catch (Exception e) {
//            log.error("gateway log exception.\n" + e);
//            return chain.filter(exchange);
//        }
//    }
//
//
//    /**
//     * 记录响应日志
//     */
//    private void responseLog(String requestId, byte[] content, HttpStatus statusCode) {
//        String responseBody = new String(content, StandardCharsets.UTF_8);
//        StringBuilder builder = new StringBuilder();
//        builder.append("\n\n 以下是请求：【").append(requestId).append("】对应的响应的日志输出：");
//        builder.append("\n 响应时间：").append(LocalDateTime.now());
//        builder.append("\n 状态码：").append(statusCode);
//        builder.append("\n 响应结果：").append(responseBody);
//        builder.append("\n");
//        log.info(builder.toString());
//    }

    /**
     *
     */
    private void doAuthentication(ServerHttpRequest request) {
        HttpHeaders requestHeaders = request.getHeaders();
        String authentication = requestHeaders.getFirst(AuthenticationConstant.AUTHENTICATION_HEADER);
        if (CharSequenceUtil.isBlank(authentication)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限,请登录");
        }
        if (!MyJWTUtil.verifyToken(authentication)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限,请登录");
        }
        // 校验JWT过期时间
        if (MyJWTUtil.getClaims(authentication).getExpiration().before(new Date())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限,请登录");
        }
        String userId = (String) MyJWTUtil.getPayLoadItem(authentication, "userId");
        String userJson = stringRedisTemplate.opsForValue().get(UserConstant.USER_ATTRIBUTE_REDIS_KEY + userId);
        if (CharSequenceUtil.isBlank(userJson)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限,请登录");
        }
    }


    /**
     * 请求日志记录
     */
    private void requestLog(ServerHttpRequest request) {
        RequestPath path = request.getPath();
        String requestId = request.getId();
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        InetSocketAddress localAddress = request.getLocalAddress();
        HttpMethod method = request.getMethod();
        StringBuilder builder = new StringBuilder();
        builder.append("\n\n 以下是请求：【").append(requestId).append("】的日志输出：");
        builder.append("\n 请求时间：").append(LocalDateTime.now());
        builder.append("\n 请求方法：").append(method);
        builder.append("\n 请求发送地址：").append(localAddress);
        builder.append("\n 请求路径：").append(path);
        builder.append("\n 请求参数：").append(JSONUtil.toJsonStr(queryParams));
        builder.append("\n");
        log.info(builder.toString());
    }

    /**
     * 过滤器的优先级
     */
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}